﻿/**
 * Copyright (C) 2011 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Spi;
using System.Diagnostics;
using NGuice.Inject.Internal.Util;

namespace NGuice.Inject.Internal
{
    /// <summary>
    /// C#无法使用泛型通配符，使用标记接口解决此问题
    /// </summary>
    public interface IBindingImpl : IBinding 
    {
        InternalFactory GetInternalFactory();

        Scoping Scoping
        {
            get;
        }
    }

    /// <summary>
    /// binding绑定接口的抽象实现
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class BindingImpl<T>: IBinding<T>, IBindingImpl
    {
        private readonly InjectorImpl injector;
        
        private readonly Key key;
        
        private readonly object source;
        
        private readonly Scoping scoping;
        
        private readonly InternalFactory<T> internalFactory;

         public BindingImpl(InjectorImpl injector, Key key, object source, InternalFactory<T> internalFactory, Scoping scoping)
         {
            this.injector = injector;
            this.key = key;
            this.source = source;
            this.internalFactory = internalFactory;
            this.scoping = scoping;
         }

        protected BindingImpl(object source, Key key, Scoping scoping) 
        {
            this.internalFactory = null;
            this.injector = null;
            this.source = source;
            this.key = key;
            this.scoping = scoping;
        }

        public InjectorImpl Injector
        {
            get
            {
                return injector;
            }
        }
        
        public Key<T> Key 
        {
            get
            {
                return (Key<T>)key;
            }
        }

        public virtual object Source 
        {
          get
          {
              return source;
          }
        }

        private volatile IProvider<T> provider;

        public virtual IProvider<T> Provider 
        {
            get 
            {
                 if (provider == null) 
                 {
                    if (injector == null) 
                    {
                        throw new NotSupportedException("Provider not supported for module bindings");
                    }
                    provider = injector.GetProvider((Key<T>)key);
                 }
                 return provider;
            }
        }

        public virtual InternalFactory<T> InternalFactory 
        {
            get 
            {
                return internalFactory;
            }
        }

        public virtual Scoping Scoping 
       {
            get 
            {
                return scoping;
            }
       }

        public virtual bool IsConstant() 
        {
            return this is IInstanceBinding<T>;
        }

        public virtual V AcceptVisitor<V>(IElementVisitor<V> visitor) 
        {
            return visitor.Visit(this);
        }

        public virtual V AcceptScopingVisitor<V>(IBindingScopingVisitor<V> visitor) 
        {
            return scoping.AcceptVisitor(visitor);
        }

        protected internal virtual BindingImpl<T> WithScoping(Scoping scoping) 
        {
            throw new AssertionError();
        }

        protected internal virtual BindingImpl<T> WithKey(Key<T> key) 
        {
            throw new AssertionError();
        }

        public override string ToString() 
        {
            return new ToStringBuilder(typeof(IBinding<T>))
                                .Add("key", key)
                                .Add("scope", scoping)
                                .Add("source", source)
                                .ToString();
        }

        public abstract V AcceptTargetVisitor<V>(IBindingTargetVisitor<T, V> visitor);

        public abstract void ApplyTo(IBinder binder);

        Key IBinding.Key
        {
            get { return this.key; }
        }

        IProvider IBinding.Provider
        {
            get { return this.provider; }
        }


        public InternalFactory GetInternalFactory()
        {
            return this.InternalFactory;
        }
    }
}
